let ws = {};

const tip = {
    render(option) {
        this.option = option;
        this.html = "";
        this.createContainer();
    },
    createContainer() {
        var that = this;
        that.html += '<div id="' + that.option.id + '" class="tip-container">';
        that.html += '<div class="tip-content">';
        that.html += '</div>';
        that.html += '</div>';
    },
    show(opt) {
        this.remove()
        $("body").append(this.html);
        var that = this,
            container = $("#" + that.option.id);
        container.addClass("tip-" + opt.type);
        container.stop().animate({
            top: 110
        }, 200, 'linear', function() {
            setTimeout(function() {
                that.remove();
            }, 1000);
        });

        return this;
    },
    remove() {
        $("#" + this.option.id).remove();
    },
    setText(htmlText) {
        $("#" + this.option.id + " .tip-content").text(htmlText);
        return this;
    }
};
const port = {
    ISPAUSE: true,
    datline: 0,
    timer: "",
    TIMERS: "",
    render: function(data) {
        data = data || {};
        this.init();
        this.createInput();
    },
    init: function() {
        $("#mianWidget").jqxSplitter({
            width: "100%",
            height: "100%",
            orientation: 'horizontal',
            panels: [{
                size: "78%",
                collapsible: false
            }]
        });
        $('#leftWidget').jqxSplitter({
            width: "100%",
            height: "100%",
            // resizable: false,
            panels: [{
                size: "250px"
            }, {
                collapsible: false
            }]
        });
        $('#rightWidget').jqxSplitter({
            width: '100%',
            height: '100%',
            panels: [{
                size: '84%',
                collapsible: false
            }]
        });

        $("select").focus(function() {
            $(this).siblings("i.edge").addClass("tf180")
        }).blur(function() {
            $(this).siblings("i.edge").removeClass("tf180")
        }).change(function() {
            $(this).siblings("i.edge").removeClass("tf180")
        });

        $(".tab-title li").click(function() {
            var that = $(this),
                index = that.index(),
                content = that.parent("ul.tab-title").siblings("div.tab-content");
            that.siblings("li").removeClass("li-this");
            that.addClass("li-this");
            $.each(content.children(), function(i, e) {
                $(e)[i === index ? "addClass" : "removeClass"]("show");
            });
        });

        $("a[event='restoration']").click(function() {
            port.input.receive.setValue("0");
            port.input.send.setValue("0");
        })
    },
    createInput() {
        this.input = {
            portName: this.setInput("input[name='portName']"),
            address: this.setInput("input[name='address']"),
            baudRate: this.setInput("select[name='baudRate']"),
            parity: this.setInput("select[name='parity']"),
            dataBits: this.setInput("select[name='dataBits']"),
            stopBits: this.setInput("select[name='stopBits']"),
            showTime: this.setCheckBox("input[name='showTime']", this.showReceiveTime),
            showHexadecimal: this.setCheckBox("input[name='showHexadecimal']"),
            fontColor: this.setCheckBox("input[name='fontColor']", this.changeWorkAreaBg),
            receivedarea: this.setTextarea("textarea[name='receivedarea']"),
            receive: this.setInput("input[name='receive']"),
            send: this.setInput("input[name='send']"),
            hexadecimal: this.setCheckBox("input[name='hexadecimal']", this.hexadecimalFn),
            lineFeed: this.setCheckBox("input[name='lineFeed']", this.lineFeedFn),
            sendbytes: this.setInput("input[name='sendbytes']", this.sendbytesFn),
            timed: this.setCheckBox("input[name='timed']", this.timedFn),
            period: this.setInput("input[name='ms']", "1000", this.periodFn)
        };
        this.button = {
            openport: this.setButton("button[event='openport']", this.openHttpPort),
            pauseport: this.setButton("button[event='pauseport']", this.pauseHttpPort),
            closeport: this.setButton("button[event='closeport']", this.closeHttpPort),
            saveport: this.setButton("button[event='saveport']", this.saveHttpPort),
            sendbtn: this.setButton("button[event='sendbtn']", this.sendHttpPort),
            stop: this.setButton("button[event='stop']", this.stopSend),
            clear: this.setButton("button[event='clear']"),
            clearData: this.setButton("button[event='clearData']", this.clearDataFn),
        };
        this.table = this.setTable("#multiple-sending", this.tableClick);
        //禁用表格按钮
        this.table.disabled(true);
    },
    upData(d) {
        d = d || {};
        this.input.portName.setValue(d.name);
        this.input.address.setValue(d.opts?.address);
        this.input.baudRate.setValue(d.opts?.baudRate);
        this.input.parity.setValue(d.opts?.parity);
        this.input.dataBits.setValue(d.opts?.dataBits);
        this.input.stopBits.setValue(d.opts?.stopBits);
    },
    setInput(el, fn) {
        return {
            el: $(el),
            setValue: function(v) {
                return this.el.val(v);
            },
            getValue: function() {
                return this.el.val();
            },
            disabled: function(v) {
                this.el.prop("disabled", v);
            },
            setSendBytesLength(v) {
                var oldLength = +(port.input.send.getValue());
                port.input.send.setValue(oldLength + v);
            }
        };
    },
    setCheckBox(el, fn) {
        var $el = $(el);
        var t = {
            change: $el.change(function() {
                fn && fn(this, t.getValue())
            }),
            setValue: function(v) {
                $el.prop("checked", v);
            },
            getValue: function() {
                return $el.is(':checked');
            }
        };
        return t;
    },
    setTextarea(el, fn) {
        return {
            el: $(el),
            setValue: function(v) {
                var str = this.getValue() + "\n",
                    time = this.isAddTime();
                v = this.hexadecimal(v);
                if (port.datline++ >= 1000) {
                    this.clear();
                    str = "";
                    port.datline = 0
                }
                if (time) {
                    str += time + " " + v;
                } else {
                    str += v;
                };

                this.el.val(str);
                this.setBytrsLength(v);
                this.scrollTop();
            },
            getValue: function() {
                return this.el.val();
            },
            clear() {
                this.el.val(" ")
            },
            changeBg: function(v) {
                this.el[v ? "addClass" : "removeClass"]("dark")
            },
            scrollTop: function() {
                this.el.scrollTop(this.el[0].scrollHeight);
            },
            isAddTime() {
                if (port.input.showTime.getValue()) {
                    return setTime()
                } else {
                    return false
                }
            },
            hexadecimal(data) {
                if (data instanceof ArrayBuffer) {
                    if (port.input.showHexadecimal.getValue()) {
                        return buf2hex(data) + " "
                    } else {
                        return buf2string(data)
                    }
                }
                return data
            },
            setBytrsLength(v) {
                var length = (v.replace(/\s+/g, "")).length,
                    oldLength = +(port.input.receive.getValue());
                port.input.receive.setValue(oldLength + length);
            }
        }
    },
    setButton(el, fn = function() {}) {
        var $el = $(el);
        return {
            el: $el,
            click: $el.click(fn),
            show: function() {
                $el.show();
            },
            hide: function() {
                $el.hide();
            },
            disabled: function(v) {
                $el.prop("disabled", v);
            }
        }
    },
    setTable(el, fn) {
        let $el = $(el),
            autoSend = $el.find("input[name='autoSend']"),
            num = $el.find("table button").length;
        $el.on("click", "table button", function() {
            let input = $(this).parent("td").siblings(),
                check = $(input[0]).find("input").is(':checked'),
                str = $(input[1]).find("input").val(),
                ms = $el.find("input[name='autoInterval']").val();
            if (!str) {
                return tip.show({ type: "error" }).setText("发送数据不能为空!");
            }
            if (check) {
                var isBol = /^[A-Fa-f0-9]{2,128}$/.test(str);
                if (!isBol || (str.length % 2 != 0)) {
                    return tip.show({ type: "error" }).setText("请输入十六进制数据!")
                }
            }
            fn && fn(autoSend.is(':checked'), +ms, check, str)
        });
        autoSend.change(function() {
            clearIntervalFn();
        });
        $el.find("button[event='addTr']").click(function() {
            clearIntervalFn();
            var b = "<button";
            if ($("button[event='openport']").css("display") != "none") {
                b += " disabled";
            }
            b += `>${++num}</button>`;
            $el.find("table tbody").append(`<tr> <td> <input type="checkbox"> </td> <td> <input type="text" value=""></td><td>${b}</td></tr>`)
        });
        $el.find("button[event='subtractTr']").click(function() {
            clearIntervalFn();
            if ((num--) <= 0) num = 0;
            $el.find("table tbody tr").eq(-1).remove();
        });

        function clearIntervalFn() {
            port.TIMERS && clearInterval(port.TIMERS);
        }
        return {
            disabled: function(v) {
                $.each($el.find("tbody button"), function(t, i) {
                    $(i).prop("disabled", v);
                });
            },
            clearIntervalFn,
        }
    },
    getParame() {
        return {
            portName: port.input.portName.getValue(),
            address: port.input.address.getValue(),
            baudRate: +(port.input.baudRate.getValue()),
            parity: port.input.parity.getValue(),
            dataBits: +(port.input.dataBits.getValue()),
            stopBits: +(port.input.stopBits.getValue()),
            timeout: 1000000000,
        }
    },


    changeWorkAreaBg(t, e) {
        port.input.receivedarea.changeBg(e);
    },
    openHttpPort() {
        var parame = port.getParame();
        if (!parame.address || !parame.baudRate || !parame.parity || !parame.dataBits || !parame.stopBits) {
            return false
        };
        $.ajax({
            url: '/xapi/openport',
            type: "POST",
            headers: {
                'Content-Type': 'application/json;charset=utf-8'
            },
            data: JSON.stringify(parame),
            success: function(data) {
                if (data.code === 0) {
                    port.ISPAUSE = true;
                    ws = new WebSocket(getWSpath() + '?id=' + parame.portName);
                    ws.binaryType = 'arraybuffer';
                    ws.onopen = () => {
                        port.input.portName.disabled(true);
                        port.input.address.disabled(true);
                        port.input.baudRate.disabled(true);
                        port.input.parity.disabled(true);
                        port.input.dataBits.disabled(true);
                        port.input.stopBits.disabled(true);
                        port.button.openport.hide();
                        port.button.pauseport.show();
                        port.button.closeport.show();
                        port.button.sendbtn.disabled(false);
                        port.table.disabled(false);
                    };
                    ws.onmessage = (msg) => {
                        if (!port.ISPAUSE) return;
                        port.input.receivedarea.setValue(msg.data)
                    }
                }
            }
        });

    },
    pauseHttpPort() {
        port.ISPAUSE = !port.ISPAUSE;
        $("button[event='pauseport']").text(port.ISPAUSE ? "暂停" : "继续");
    },
    closeHttpPort() {
        $.ajax({
            url: '/xapi/closeport',
            type: "POST",
            // url: '/api/a_openport.json',
            // type: "GET",
            success: function(data) {
                ws.close();
                port.ISPAUSE = true;
                port.input.portName.disabled(false);
                port.input.address.disabled(false);
                port.input.baudRate.disabled(false);
                port.input.parity.disabled(false);
                port.input.dataBits.disabled(false);
                port.input.stopBits.disabled(false);
                port.button.sendbtn.disabled(true);
                port.table.disabled(true);
                port.button.pauseport.hide();
                port.button.closeport.hide();
                port.button.openport.show();
                $("button[event='pauseport']").text("暂停");
                clearInterval(port.timer)
            },
        });
    },
    saveHttpPort() {
		var parame = port.getParame();
        $.ajax({
            url: '/xapi/ports/'+parame.portName,
            type: "POST",
            headers: {
                'Content-Type': 'application/json;charset=utf-8'
            },
            data: JSON.stringify(port.getParame()),
            success: function(data) {
				console.log(data);
                tip.show({ type: "success" }).setText("保存成功!");
            },
        });
    },
    sendHttpPort() {
        var isCheck = port.input.timed.getValue();
        var ms = port.input.period.getValue();
       var hexadecimal = port.input.hexadecimal.getValue();
       var sendbytes = port.input.sendbytes.getValue();
        if (!sendbytes) {
            return tip.show({ type: "error" }).setText("发送数据不能为空!");
        }
        if (hexadecimal) {
            var isBol = /^[A-Fa-f0-9]{2,128}$/.test(sendbytes);
            if (!isBol || (sendbytes.length % 2 != 0)) {
                return tip.show({ type: "error" }).setText("请输入十六进制数据!")
            }
        }
        if (isCheck) {
            port.timer = setInterval(() => {
                port.sendserver()
            }, ms)
            port.button.sendbtn.hide();
            port.button.stop.show();
            port.input.sendbytes.disabled(true);
            port.input.period.disabled(true);
        } else {
            port.sendserver();
        };
    },
    stopSend() {
        port.button.sendbtn.show();
        port.button.stop.hide();
        port.input.sendbytes.disabled(false);
        port.input.period.disabled(false);
        clearInterval(port.timer);
    },
    clearDataFn() {
        port.input.receivedarea.clear()
    },
    sendserver() {
        let sendbytes = port.input.sendbytes.getValue(),
            hexadecimal = port.input.hexadecimal.getValue(),
            lineFeed = port.input.lineFeed.getValue();
        var sendbytesLenght = calBytesLength(sendbytes, (hexadecimal ? sendbytes.length / 2 : sendbytes.length));
        port.input.send.setSendBytesLength(sendbytesLenght + (lineFeed ? 2 : 0));

        var sdata = {};
        sdata["name"] = port.input.portName.getValue();
        sdata["type"] = hexadecimal ? 1 : 0;
        if (hexadecimal) {
            sdata["data"] = sendbytes + ((lineFeed) ? "" : "");
        } else {
            sdata["data"] = sendbytes + ((lineFeed) ? "\r\n" : "");
        }
        ws.send(JSON.stringify(sdata));
    },
    sendMultiterm(hex, val) {
        console.log(val)
        var length = calBytesLength(val, (hex ? val.length / 2 : val.length)),
            sdata = {
                name: port.input.portName.getValue(),
                type: hex ? 1 : 0,
                data: val + ""
            };
        port.input.send.setSendBytesLength(length);
        ws.send(JSON.stringify(sdata));
    },
    tableClick(autoSend, ms, checkHex, val) {
        port.table.clearIntervalFn();
        if (autoSend) {
            port.TIMERS = setInterval(() => {
                port.sendMultiterm(checkHex, val);
            }, ms);
        } else {
            port.sendMultiterm(checkHex, val);
        }

    }
}
tip.render({ id: "tip" });
port.render();
$.ajax({
    url: '/xapi/ports/' + getQueryString("name"),
    type: "GET",
    success: function(data) {
        port.upData(data)
    },
    error: function() {
        port.upData({})
    },
});

function getQueryString(name) {
    let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
    let r = window.location.search.substr(1).match(reg);
    if (r != null) {
        return decodeURIComponent(r[2]);
    };
    return null;
}

function setTime() {
    var date = new Date();
    let h = new Date(date).getHours();
    h = h < 10 ? "0" + h : h;
    let m = new Date(date).getMinutes();
    m = m < 10 ? "0" + m : m;
    let s = new Date(date).getSeconds();
    s = s < 10 ? "0" + s : s;
    let ms = new Date(date).getMilliseconds();
    if (ms < 10) {
        ms = "00" + ms
    }
    if (ms >= 10 && ms < 100) {
        ms = "0" + ms
    }
    if (ms >= 100) {
        ms = ms
    }
    return h + ":" + m + ":" + s + ":" + ms;
}

function calBytesLength(val, len) {
    var str = new String(val);
    var bytesCount = 0;
    for (var i = 0, n = len; i < n; i++) {
        var c = str.charCodeAt(i);
        if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) {
            bytesCount += 1;
        } else {
            bytesCount += 2;
        }
    }
    return bytesCount;
}

function getWSpath() {
    var loc = window.location,
        new_uri;
    if (loc.protocol === "https:") {
        new_uri = "wss:";
    } else {
        new_uri = "ws:";
    }
    //new_uri += "//" + loc.host + "/ws";
    new_uri = "ws://192.168.0.10/ws"
    return new_uri;
}

function stringToBytes(str) {
    return hexToBytes(stringToHex(str));
}

// Convert a ASCII string to a hex string
function stringToHex(str) {
    return str.split("").map(function(c) {
        return ("0" + c.charCodeAt(0).toString(16)).slice(-2);
    }).join("");
}

function hexToBytes(hex) {
    for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
    return bytes;
}

// Convert a hex string to a ASCII string
function hexToString(hexStr) {
    var hex = hexStr.toString(); //force conversion
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

function buf2string(buffer) {
    var hex = Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
    return hexToString(hex).toUpperCase();
}

function buf2hex(buffer) { // buffer is an ArrayBuffer
    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(' ');
}